﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Web;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ifunction.BaiduBCS.SDK
{
    partial class BaiduBCSClient
    {
        /// <summary>
        /// Creates the storage object.
        /// <example>
        /// The following code would show you how to <c>create</c>, <c>get</c> and <c>delete</c> <c>object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // Firstly, we need to create a file for upload test later.
        /// //Supposed that the test machine has disk D and application has enough permission to operate on that.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world.");
        ///
        /// // Read/Load the bytes for creating object.
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // Upload bytes and file information
        /// var md5 = client.CreateStorageObject(new StorageObjectUploadRequest
        ///       {
        ///           Bytes = content,
        ///           BucketName = bucket,
        ///           ObjectName = objectName,
        ///           DownloadFileName = "DownloadName.dat",
        ///           ContentType = "text/plain",
        ///           IsSuperFile = false,
        ///           Permission = StorageObjectPermission.PublicReadAndWrite
        ///       });
        ///
        /// // We try to get meta data for the uploaded file.
        /// var meta = client.GetStorageObjectMetaInfo(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We try to get whole byte data for the uploaded file.
        /// var data = client.GetStorageObject(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We also can save file in local by specific path
        /// client.SaveStorageObjectAs(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\DownloadedTest.txt");
        ///
        /// // Test file needs to be deleted.
        /// client.DeleteStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="uploadRequest">The upload request.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">GetBucketList</exception>
        public string CreateStorageObject(StorageObjectUploadRequest uploadRequest)
        {
            const string httpVerb = "POST";

            try
            {
                CheckBucketObject(uploadRequest);
                uploadRequest.Bytes.CheckNullObject("uploadRequest.Bytes");

                string featureUrl = CreateFeatureUrl(uploadRequest.BucketName, uploadRequest.ObjectName);

                if (uploadRequest.UseMD5AsFileName)
                {
                    featureUrl += "?renametype=md5";
                }

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, uploadRequest.BucketName, uploadRequest.ObjectName);

                //Fill headers
                FillHttpRequestHeader(httpRequest, "Cache-Control", uploadRequest.CacheControl);
                httpRequest.ContentType = string.IsNullOrWhiteSpace(uploadRequest.ContentType) ? "binary/octet-stream" : uploadRequest.ContentType;
                FillHttpRequestHeader(httpRequest, "Content-Disposition", string.IsNullOrWhiteSpace(uploadRequest.DownloadFileName) ? null : string.Format("[attachement|filename={0}]", uploadRequest.DownloadFileName));
                FillHttpRequestHeader(httpRequest, "Expires", uploadRequest.ExpiredStamp == null ? null : uploadRequest.ExpiredStamp.Value.ToUniversalTime()
    .ToString("ddd, dd MMM yyyy HH:mm:ss 'GMT'"));
                FillHttpRequestHeader(httpRequest, "x-bs-acl", uploadRequest.Permission.GetEnumContractText());

                Dictionary<string, byte[]> fileDictionary = new Dictionary<string, byte[]>();
                fileDictionary.Add(uploadRequest.ObjectName, uploadRequest.Bytes);

                httpRequest.FillFileData(null, fileDictionary, null);

                HttpStatusCode statusCode;
                WebHeaderCollection responseHeader;
                var responseText = httpRequest.ReadResponseAsText(Encoding.UTF8, out statusCode, out responseHeader);

                TryThrowExceptionByStatusCode(statusCode, responseText);

                return responseHeader.Get("Content-MD5");
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("CreateStorageObject", ex);
            }
        }

        /// <summary>
        /// Copies the storage object.
        /// <example>
        /// <c>Usage Sample:</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your target object name (file name) in your code
        /// string targetObjectName = "copiedObject.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // As a sample here, we just copy as another object in same bucket.
        /// // You can make it different in your code.
        /// client.CopyStorageObject(new StorageObjectCopyRequest
        ///            {
        ///                ObjectName = objectName,
        ///                NewObjectName = targetObjectName,
        ///                NewBucketName = bucket,
        ///                BucketName = bucket
        ///            });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="copyRequest">The copy request.</param>
        /// <exception cref="System.InvalidOperationException">GetStorageObjectList</exception>
        public void CopyStorageObject(StorageObjectCopyRequest copyRequest)
        {
            const string httpVerb = "PUT";

            try
            {
                CheckBucketObject(copyRequest);

                copyRequest.NewBucketName.CheckNullObject("copyRequest.NewBucketName");
                copyRequest.NewObjectName.CheckNullObject("copyRequest.NewObjectName");
                copyRequest.NewBucketName = copyRequest.NewBucketName.ToLowerInvariant();
                copyRequest.NewObjectName = copyRequest.NewObjectName.ToLowerInvariant();

                string featureUrl = CreateFeatureUrl(copyRequest);

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, copyRequest.NewBucketName, copyRequest.NewObjectName);

                FillHttpRequestHeader(httpRequest, "x-bs-copy-source", baseServiceUrl + featureUrl);
                FillHttpRequestHeader(httpRequest, "x-bs-copy-source-tag", copyRequest.MD5MatchRequired);

                HttpStatusCode statusCode;
                var responseText = httpRequest.ReadResponseAsText(Encoding.UTF8, out statusCode);

                TryThrowExceptionByStatusCode(statusCode, responseText);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("CopyStorageObject", ex);
            }
        }

        /// <summary>
        /// Creates the super storage object.
        /// <example>
        /// The following code would show you how to <c>create</c> <c>super object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "mergeTest.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // This is just a test. We need to get bytes.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world. Large File!!");
        ///
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // If you set [deletePartsWhenFinish] as false, all small pieces of large file would be remained.
        /// // Default value for [deletePartsWhenFinish ] is [True].
        /// var md5 = client.CreateSuperStorageObject(new StorageObjectUploadRequest
        ///          {
        ///              BucketName = bucket,
        ///              ObjectName = objectName,
        ///              Bytes = content
        ///          }, 10, true);
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="uploadRequest">The upload request.</param>
        /// <param name="packageSizeInByte">The package size in byte.</param>
        /// <param name="deletePartsWhenFinish">if set to <c>true</c> [delete parts when finish].</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.InvalidOperationException">CreateSuperStorageObject</exception>
        public string CreateSuperStorageObject(StorageObjectUploadRequest uploadRequest, int packageSizeInByte, bool deletePartsWhenFinish = true)
        {
            if (packageSizeInByte < 1)
            {
                packageSizeInByte = 100;
            }

            try
            {
                this.CheckBucketObject(uploadRequest);
                uploadRequest.Bytes.CheckNullObject("uploadRequest.Bytes");

                StorageObjectMergeRequest mergeRequest = new StorageObjectMergeRequest
                {
                    BucketName = uploadRequest.BucketName,
                    ObjectName = uploadRequest.ObjectName,
                    IsSuperFile = true,
                    ObjectParts = new List<StorageObjectIdentity>()
                };

                var round = 0;
                var lengthLeft = uploadRequest.Bytes.LongLength - round * packageSizeInByte;
                byte[] buffer = new byte[packageSizeInByte];
                while (lengthLeft > 0)
                {
                    var objectName = uploadRequest.ObjectName + round.ToString();

                    var length = lengthLeft > packageSizeInByte ? packageSizeInByte : lengthLeft;
                    Array.Copy(uploadRequest.Bytes, round * packageSizeInByte, buffer, 0, length);

                    var md5 = this.CreateStorageObject(new StorageObjectUploadRequest
                               {
                                   BucketName = uploadRequest.BucketName,
                                   Bytes = buffer,
                                   ContentType = uploadRequest.ContentType,
                                   CacheControl = uploadRequest.CacheControl,
                                   IsSuperFile = false,
                                   MD5 = uploadRequest.MD5,
                                   ObjectName = objectName
                               });

                    if (!string.IsNullOrWhiteSpace("md5"))
                    {
                        mergeRequest.ObjectParts.Add(new StorageObjectIdentity
                        {
                            BucketName = uploadRequest.BucketName,
                            ObjectName = objectName,
                            MD5 = md5
                        });
                    }

                    round++;
                    lengthLeft = uploadRequest.Bytes.Length - round * packageSizeInByte;
                }

                var result = MergeObjectAsSuperStorageObject(mergeRequest);

                if (deletePartsWhenFinish)
                {
                    foreach (var one in mergeRequest.ObjectParts)
                    {
                        this.DeleteStorageObject(new StorageObjectIdentity
                        {
                            BucketName = one.BucketName,
                            ObjectName = one.ObjectName
                        });
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("CreateSuperStorageObject", ex);
            }
        }

        /// <summary>
        /// Creates the super storage object.
        /// <example>
        /// The following code would show you how to <c>create</c> <c>super object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "mergeTestByPath.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // This is just a test. We need to get bytes.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world. Large File!!");
        ///
        /// // If you set [deletePartsWhenFinish] as false, all small pieces of large file would be remained.
        /// // Default value for [deletePartsWhenFinish ] is [True].
        /// var md5 = client.CreateSuperStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\" + objectName, 10, true);
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="identity">The identity.</param>
        /// <param name="filePath">The file path.</param>
        /// <param name="packageSizeInByte">The package size in byte.</param>
        /// <param name="deletePartsWhenFinish">if set to <c>true</c> [delete parts when finish].</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.IO.FileNotFoundException"></exception>
        /// <exception cref="System.InvalidOperationException">CreateSuperStorageObject</exception>
        public string CreateSuperStorageObject(StorageObjectIdentity identity, string filePath, int packageSizeInByte, bool deletePartsWhenFinish = true)
        {

            if (packageSizeInByte < 1)
            {
                packageSizeInByte = 100;
            }

            try
            {
                identity.CheckNullObject("identity");
                filePath.CheckNullOrEmptyString("filePath");

                FileInfo fileInfo = new FileInfo(filePath);

                if (!fileInfo.Exists)
                {
                    throw new FileNotFoundException(filePath);
                }

                var fileLength = fileInfo.Length;

                StorageObjectMergeRequest mergeRequest = new StorageObjectMergeRequest
                {
                    BucketName = identity.BucketName,
                    ObjectName = identity.ObjectName,
                    IsSuperFile = true,
                    ObjectParts = new List<StorageObjectIdentity>()
                };

                var round = 0;
                var lengthLeft = fileLength - round * packageSizeInByte;
                byte[] buffer = new byte[packageSizeInByte];

                using (FileStream stream = new FileStream(filePath, FileMode.Open))
                {
                    while (lengthLeft > 0)
                    {
                        var objectName = identity.ObjectName + round.ToString();

                        int length = (int)(lengthLeft > packageSizeInByte ? packageSizeInByte : lengthLeft);
                        stream.Read(buffer, 0, length);
                        stream.Flush();

                        var md5 = this.CreateStorageObject(new StorageObjectUploadRequest
                        {
                            BucketName = identity.BucketName,
                            Bytes = buffer,
                            MD5 = identity.MD5,
                            ObjectName = objectName
                        });

                        if (!string.IsNullOrWhiteSpace("md5"))
                        {
                            mergeRequest.ObjectParts.Add(new StorageObjectIdentity
                            {
                                BucketName = identity.BucketName,
                                ObjectName = objectName,
                                MD5 = md5
                            });
                        }

                        round++;
                        lengthLeft = fileLength - round * packageSizeInByte;
                    }
                }

                var result = MergeObjectAsSuperStorageObject(mergeRequest);

                if (deletePartsWhenFinish)
                {
                    foreach (var one in mergeRequest.ObjectParts)
                    {
                        this.DeleteStorageObject(new StorageObjectIdentity
                        {
                            BucketName = one.BucketName,
                            ObjectName = one.ObjectName
                        });
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("CreateSuperStorageObject", ex);
            }
        }

        /// <summary>
        /// Merges the object as super storage object.
        /// Commonly, this is used by method <see cref="CreateSuperStorageObject"/>, please refer sample in that method.
        /// </summary>
        /// <param name="mergeRequest">The merge request.</param>
        /// <returns>System.String.</returns>
        /// <exception cref="System.ArgumentException">mergeRequest.ObjectParts</exception>
        /// <exception cref="System.InvalidOperationException">MergeObjectAsSuperStorageObject</exception>
        public string MergeObjectAsSuperStorageObject(StorageObjectMergeRequest mergeRequest)
        {
            const string httpVerb = "PUT";

            try
            {
                CheckBucketObject(mergeRequest);
                mergeRequest.ObjectParts.CheckNullObject("mergeRequest.ObjectParts");

                if (mergeRequest.ObjectParts.Count == 0)
                {
                    throw new ArgumentException("mergeRequest.ObjectParts");
                }

                string featureUrl = CreateFeatureUrl(mergeRequest);

                var httpRequest = this.CreateHttpRequest(featureUrl + "?superfile=1", httpVerb, mergeRequest.BucketName, mergeRequest.ObjectName);

                var jsonText = mergeRequest.ToJson().ToString();
                httpRequest.FillData(httpVerb, jsonText);

                WebHeaderCollection header;
                HttpStatusCode statusCode;
                var responseText = httpRequest.ReadResponseAsText(Encoding.UTF8, out statusCode, out header);

                TryThrowExceptionByStatusCode(statusCode, responseText);
                return header.Get("Content-MD5");
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("MergeObjectAsSuperStorageObject", ex);
            }
        }

        /// <summary>
        /// Gets the storage object.
        /// <example>
        /// The following code would show you how to <c>create</c>, <c>get</c> and <c>delete</c> <c>object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // Firstly, we need to create a file for upload test later.
        /// //Supposed that the test machine has disk D and application has enough permission to operate on that.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world.");
        ///
        /// // Read/Load the bytes for creating object.
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // Upload bytes and file information
        /// var md5 = client.CreateStorageObject(new StorageObjectUploadRequest
        ///       {
        ///           Bytes = content,
        ///           BucketName = bucket,
        ///           ObjectName = objectName,
        ///           DownloadFileName = "DownloadName.dat",
        ///           ContentType = "text/plain",
        ///           IsSuperFile = false,
        ///           Permission = StorageObjectPermission.PublicReadAndWrite
        ///       });
        ///
        /// // We try to get meta data for the uploaded file.
        /// var meta = client.GetStorageObjectMetaInfo(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We try to get whole byte data for the uploaded file.
        /// var data = client.GetStorageObject(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We also can save file in local by specific path
        /// client.SaveStorageObjectAs(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\DownloadedTest.txt");
        ///
        /// // Test file needs to be deleted.
        /// client.DeleteStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="downloadRequest">The download request.</param>
        /// <returns>StorageObjectData.</returns>
        public StorageObjectData GetStorageObject(StorageObjectDownloadRequest downloadRequest)
        {
            const string httpVerb = "GET";

            try
            {
                CheckBucketObject(downloadRequest);

                var result = new StorageObjectData
                       {
                           BucketName = downloadRequest.BucketName,
                           ObjectName = downloadRequest.ObjectName
                       };

                string featureUrl = CreateFeatureUrl(downloadRequest);

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, downloadRequest.BucketName, downloadRequest.ObjectName);

                FillHttpRequestHeader(httpRequest, "Range", downloadRequest.DataRange);
                FillHttpRequestHeader(httpRequest, "If-None-Match", downloadRequest.MD5Match);
                FillHttpRequestHeader(httpRequest, "x-bs-version", downloadRequest.Version);

                WebHeaderCollection header;
                HttpStatusCode statusCode;
                result.Bytes = httpRequest.ReadResponseAsBytes(out statusCode, out header);
                result.MD5 = header.Get("etag");

                TryThrowExceptionByStatusCode(statusCode, null);

                return result;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("GetStorageObject", ex);
            }
        }

        /// <summary>
        /// Saves the storage object as.
        /// <example>
        /// The following code would show you how to <c>create</c>, <c>get</c> and <c>delete</c> <c>object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // Firstly, we need to create a file for upload test later.
        /// //Supposed that the test machine has disk D and application has enough permission to operate on that.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world.");
        ///
        /// // Read/Load the bytes for creating object.
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // Upload bytes and file information
        /// var md5 = client.CreateStorageObject(new StorageObjectUploadRequest
        ///       {
        ///           Bytes = content,
        ///           BucketName = bucket,
        ///           ObjectName = objectName,
        ///           DownloadFileName = "DownloadName.dat",
        ///           ContentType = "text/plain",
        ///           IsSuperFile = false,
        ///           Permission = StorageObjectPermission.PublicReadAndWrite
        ///       });
        ///
        /// // We try to get meta data for the uploaded file.
        /// var meta = client.GetStorageObjectMetaInfo(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We try to get whole byte data for the uploaded file.
        /// var data = client.GetStorageObject(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We also can save file in local by specific path
        /// client.SaveStorageObjectAs(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\DownloadedTest.txt");
        ///
        /// // Test file needs to be deleted.
        /// client.DeleteStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="downloadRequest">The download request.</param>
        /// <param name="filePath">The file path.</param>
        /// <exception cref="System.InvalidOperationException">SaveStorageObjectAs</exception>
        public void SaveStorageObjectAs(StorageObjectDownloadRequest downloadRequest, string filePath)
        {
            try
            {
                filePath.CheckNullOrEmptyString("filePath");
                var data = GetStorageObject(downloadRequest);

                if (data.Bytes != null)
                {
                    File.WriteAllBytes(filePath, data.Bytes);
                }
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("SaveStorageObjectAs", ex);
            }
        }

        /// <summary>
        /// Gets the storage object meta information.
        /// <example>
        /// The following code would show you how to <c>create</c>, <c>get</c> and <c>delete</c> <c>object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // Firstly, we need to create a file for upload test later.
        /// //Supposed that the test machine has disk D and application has enough permission to operate on that.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world.");
        ///
        /// // Read/Load the bytes for creating object.
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // Upload bytes and file information
        /// var md5 = client.CreateStorageObject(new StorageObjectUploadRequest
        ///       {
        ///           Bytes = content,
        ///           BucketName = bucket,
        ///           ObjectName = objectName,
        ///           DownloadFileName = "DownloadName.dat",
        ///           ContentType = "text/plain",
        ///           IsSuperFile = false,
        ///           Permission = StorageObjectPermission.PublicReadAndWrite
        ///       });
        ///
        /// // We try to get meta data for the uploaded file.
        /// var meta = client.GetStorageObjectMetaInfo(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We try to get whole byte data for the uploaded file.
        /// var data = client.GetStorageObject(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We also can save file in local by specific path
        /// client.SaveStorageObjectAs(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\DownloadedTest.txt");
        ///
        /// // Test file needs to be deleted.
        /// client.DeleteStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="downloadRequest">The download request.</param>
        /// <returns>StorageObjectMetaInfo.</returns>
        /// <exception cref="System.InvalidOperationException">GetStorageObject</exception>
        public StorageObjectMetaInfo GetStorageObjectMetaInfo(StorageObjectDownloadRequest downloadRequest)
        {
            const string httpVerb = "HEAD";

            try
            {
                CheckBucketObject(downloadRequest);

                var result = new StorageObjectMetaInfo
                {
                    BucketName = downloadRequest.BucketName,
                    ObjectName = downloadRequest.ObjectName
                };

                string featureUrl = CreateFeatureUrl(downloadRequest);

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, downloadRequest.BucketName, downloadRequest.ObjectName);

                FillHttpRequestHeader(httpRequest, "If-None-Match", downloadRequest.MD5Match);
                FillHttpRequestHeader(httpRequest, "x-bs-version", downloadRequest.Version);

                WebHeaderCollection header;
                HttpStatusCode statusCode;
                httpRequest.ReadResponseAsText(Encoding.UTF8, out statusCode, out header);
                result.MD5 = header.Get("etag");
                result.CRC32 = header.Get("x-bs-meta-crc32");

                TryThrowExceptionByStatusCode(statusCode, null);

                return result;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("GetStorageObject", ex);
            }
        }

        /// <summary>
        /// Gets the storage object list.
        /// <example>
        /// <c>Usage Sample:</c>
        /// <code>
        /// <![CDATA[
        ///     // You need to assign your bucket in your code
        ///     string bucket = "TestBucket";
        ///     // You need to assign your app key and your secret key in your code
        ///     var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        ///     var result = client.GetStorageObjectList(new StorageObjectCriteria
        ///     {
        ///         Count = 100,
        ///         Start = 0,
        ///         BucketName = bucket
        ///     });
        ///
        ///     foreach (var one in result.Items)
        ///     {
        ///         Console.WriteLine(string.Format("Bucket: {0}, Object: {1}, Size: {2}, Version: {3}", one.BucketName, one.ObjectName, one.Size, one.VersionKey));
        ///     }
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="criteria">The criteria.</param>
        /// <returns>StorageObjectCollection.</returns>
        public StorageObjectSummaryCollection GetStorageObjectList(StorageObjectCriteria criteria)
        {
            const string httpVerb = "GET";

            try
            {
                criteria.CheckNullObject("criteria");
                criteria.BucketName.CheckNullOrEmptyString("criteria.BucketName");
                criteria.BucketName = criteria.BucketName.ToLowerInvariant();

                if (criteria.Start != null && criteria.Start < 0)
                {
                    criteria.Start = null;
                }

                if (criteria.Count != null && criteria.Count < 0)
                {
                    criteria.Count = null;
                }

                string featureUrl = string.Format("{0}/?start={1}&limit={2}", criteria.BucketName, criteria.Start, criteria.Count);

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, criteria.BucketName, null);
                HttpStatusCode statusCode;
                var responseText = httpRequest.ReadResponseAsText(Encoding.UTF8, out statusCode);

                TryThrowExceptionByStatusCode(statusCode, responseText);

                var result = JsonToStorageObjectCollection(responseText);

                if (result != null && result.Items.Count > 0)
                {
                    foreach (var one in result.Items)
                    {
                        one.BucketName = criteria.BucketName;
                        one.ObjectName = one.ObjectName.TrimStart('/');
                    }
                }

                return result;
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("GetStorageObjectList", ex);
            }
        }

        /// <summary>
        /// Deletes the storage object.
        /// <example>
        /// The following code would show you how to <c>create</c>, <c>get</c> and <c>delete</c> <c>object</c> in <c>BaiduBCSClient</c>
        /// <code>
        /// <![CDATA[
        /// // You need to assign your bucket in your code
        /// string bucket = "TestBucket";
        /// // You need to assign your object name (file name) in your code
        /// string objectName = "test.txt";
        /// // You need to assign your app key and your secret key in your code
        /// var client = new BaiduBCSClient("your app key", "your secret key");
        ///
        /// // Firstly, we need to create a file for upload test later.
        /// //Supposed that the test machine has disk D and application has enough permission to operate on that.
        /// File.WriteAllText(@"D:\" + objectName, "Test string \n\r Hello world.");
        ///
        /// // Read/Load the bytes for creating object.
        /// byte[] content = File.ReadAllBytes(@"D:\" + objectName);
        ///
        /// // Upload bytes and file information
        /// var md5 = client.CreateStorageObject(new StorageObjectUploadRequest
        ///       {
        ///           Bytes = content,
        ///           BucketName = bucket,
        ///           ObjectName = objectName,
        ///           DownloadFileName = "DownloadName.dat",
        ///           ContentType = "text/plain",
        ///           IsSuperFile = false,
        ///           Permission = StorageObjectPermission.PublicReadAndWrite
        ///       });
        ///
        /// // We try to get meta data for the uploaded file.
        /// var meta = client.GetStorageObjectMetaInfo(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We try to get whole byte data for the uploaded file.
        /// var data = client.GetStorageObject(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        ///
        /// // We also can save file in local by specific path
        /// client.SaveStorageObjectAs(new StorageObjectDownloadRequest
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// }, @"D:\DownloadedTest.txt");
        ///
        /// // Test file needs to be deleted.
        /// client.DeleteStorageObject(new StorageObjectIdentity
        /// {
        ///     BucketName = bucket,
        ///     ObjectName = objectName
        /// });
        /// ]]>
        /// </code>
        /// </example>
        /// </summary>
        /// <param name="storageObject">The storage object.</param>
        /// <exception cref="System.InvalidOperationException">DeleteBucket</exception>
        public void DeleteStorageObject(StorageObjectIdentity storageObject)
        {
            const string httpVerb = "DELETE";

            try
            {
                CheckBucketObject(storageObject);

                string featureUrl = CreateFeatureUrl(storageObject);

                var httpRequest = this.CreateHttpRequest(featureUrl, httpVerb, storageObject.BucketName, storageObject.ObjectName);
                HttpStatusCode statusCode;
                var responseText = httpRequest.ReadResponseAsText(null, out statusCode);

                TryThrowExceptionByStatusCode(statusCode, responseText);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("DeleteStorageObject", ex);
            }
        }

        #region Protected method

        /// <summary>
        /// Converts JSON to storage object collection.
        /// </summary>
        /// <param name="jsonString">The JSON string.
        /// <example>
        /// <![CDATA[
        /// {
        ///  "bucket": "mybucket",
        ///  "start": "1",
        ///  "limit": "3",
        ///  "object_list": [{
        ///      "version_key": "6A8D4D26BD66C7707C67B3B38FE8D0FB",
        ///      "object": "\/2011-09-02_05_10_11_009183",
        ///      "requester": "xxxxxxx",
        ///      "request_ip": "10.81.42.149",
        ///      "superfile": "0",
        ///      "size": "1395",
        ///      "parent_dir": "\/",
        ///      "is_dir": "0",
        ///      "mdatetime": "1314911411"
        ///  }, {
        ///      "version_key": "D13729019CD71DF24A44FFF81486FBFE",
        ///      "object": "\/2011-09-01_21_00_02_233530",
        ///      "requester": "xxxxxxxx",
        ///      "request_ip": "10.23.37.12",
        ///      "superfile": "0",
        ///      "size": "1307",
        ///      "parent_dir": "\/",
        ///      "is_dir": "0",
        ///      "mdatetime": "1314909460"
        ///  }, {
        ///      "version_key": "EC29997E0E241129F88C1056A2DEE48A",
        ///      "object": "\/2011-09-02_04_34_42_112357",
        ///      "requester": "xxxxxxx",
        ///      "request_ip": "10.81.42.149",
        ///      "superfile": "0",
        ///      "size": "3542",
        ///      "parent_dir": "\/",
        ///      "is_dir": "0",
        ///      "mdatetime": "1314909282"
        ///  }]
        /// }
        /// ]]>
        /// </example>
        /// </param>
        /// <returns>StorageObjectCollection.</returns>
        protected StorageObjectSummaryCollection JsonToStorageObjectCollection(string jsonString)
        {
            try
            {
                jsonString.CheckNullOrEmptyString("jsonString");

                return JsonConvert.DeserializeObject<StorageObjectSummaryCollection>(jsonString);
            }
            catch (Exception ex)
            {
                throw new InvalidOperationException("JsonToStorageObjectCollection", ex);
            }
        }

        #endregion
    }
}
